import { ByteView, U16, U32, U8, make } from "./codec";
import conn from "./conn";
import { err_msg_info } from "./ec";
import { CleanOption, CookingOption, TankOption } from "./heat_cmd";
import { addr, main } from "./proto";

export async function heat_door_ctrl(value: number) {
    return await conn.request(
        addr.MAIN,
        main.HEAT_DOOR_CTRL,
        new U8(value)
    );
}

export async function pasta_door_ctrl(value: number) {
    return await conn.request(
        addr.MAIN,
        main.PASTA_DOOR_CTRL,
        new U8(value)
    );
}

export async function extern_push_ctrl(value: number) {
    return await conn.request(
        addr.MAIN,
        main.EXTERN_PUSH_CTRL,
        new U8(value)
    );
}

export async function pick_door_ctrl(value: number) {
    return await conn.request(
        addr.MAIN,
        main.PICK_DOOR_CTRL,
        new U8(value)
    );
}

export async function rotate_ctrl(col: number, offset: number) {
    return await conn.request(
        addr.MAIN,
        main.ROTATE_CTRL,
        new U8(col),
        new U16(offset),
    );
}

export async function elevator_ctrl(row: number, offset: number) {
    return await conn.request(
        addr.MAIN,
        main.ELEVATOR_CTRL,
        new U8(row),
        new U16(offset)
    );
}

export async function fetch_ctrl(value: number) {
    return await conn.request(
        addr.MAIN,
        main.FETCH_CTRL,
        new U16(value)
    );
}

export async function pick_ctrl(col: number, row: number) {
    return await conn.request(
        addr.MAIN,
        main.PICK_CTRL,
        new U8(col),
        new U8(row)
    );
}

export async function scaner_test() {
    const msg = await conn.request(
        addr.MAIN, main.SCANER_TEST
    );
    const ec = new U8();
    const bw = new ByteView();
    msg.parseResponse(ec, bw);
    return bw.toString();
}

export async function rkc_read(reg_addr: number) {
    const msg = await conn.request(
        addr.MAIN, 
        main.RKC_READ, 
        new U16(reg_addr)
    );
    const ec = new U8();
    const value = new U16();
    msg.parseResponse(ec, value);
    return value.value;
}

export async function rkc_write(reg_addr: number, reg_value: number) {
    return await conn.request(
        addr.MAIN,
        main.RKC_WRITE,
        new U16(reg_addr),
        new U16(reg_value)
    );
}

export async function cargo_scan(col: number, row: number) {
    const buf = make(new U8(col), new U8(row));

    const msg = await conn.conn_request(
        addr.MAIN,
        main.CARGO_SCAN,
        buf
    );

    const ec = new U8();
    const bw = new ByteView();
    msg.parseResponse(ec, bw);
    
    if ((ec.value == 0) || (ec.value == 0x12) || (ec.value == 0x13)) {
        return bw.toString();
    }
    throw err_msg_info(msg.src(), ec.value);
}

export async function reset_all() {
    return await conn.request(
        addr.MAIN,
        main.RESET_ALL
    );
}

export async function tool_ctrl(type: number, val: number) {
    return await conn.request(
        addr.MAIN,
        main.TOOL_CTRL,
        new U8(type), new U8(val)
    );
}

export async function fetch_adj(type: number) {
    return await conn.request(
        addr.MAIN,
        main.FETCH_ADJ,
        new U8(type)
    );
}

export interface FetchAdjInfo {
    zero: number,
    init: number,
    left: number,
    right: number,
    heat: number,
}

export async function fetch_adj_query(): Promise<FetchAdjInfo> {
    const msg = await conn.conn_query(addr.MAIN, main.FETCH_ADJ);
    const zero = new U16();
    const init = new U16();
    const left = new U16();
    const right = new U16();
    const heat = new U16();
    msg.parseResult(zero, init, left, right, heat);
    return {
        zero: zero.value,
        init: init.value,
        left: left.value,
        right: right.value,
        heat: heat.value
    }
}

export interface RotateAdjInfo {
    pick: number,
    scan: number,
    avoid: number,
}

export async function rotate_adj_query(): Promise<RotateAdjInfo> {

    const msg = await conn.conn_query(addr.MAIN, main.ROTATE_ADJ);
    const pick = new U16();
    const scan = new U16();
    const avoid = new U16();
    msg.parseResult(pick, scan, avoid);

    return {
        pick: pick.signedValue(),
        scan: scan.signedValue(),
        avoid: avoid.signedValue(),
    }
}

export async function rotate_adj(type: number, offset: number) {
    return await conn.request(
        addr.MAIN,
        main.ROTATE_ADJ,
        new U8(type),
        new U16(offset)
    );
}

export interface ElevatorAdjInfo {
    heat_up: number,
    heat_down: number,
    pick_init: number,
    pick_up: number,
    scan: number,
}

export async function elevator_adj_query(): Promise<ElevatorAdjInfo> {

    const msg = await conn.conn_query(addr.MAIN, main.ELEVATOR_ADJ);
    const heat_up = new U16();
    const heat_down = new U16();
    const pick_init = new U16();
    const pick_up = new U16();
    const scan = new U16();
    msg.parseResult(heat_up, heat_down, pick_init, pick_up, scan);
    return {
        heat_up: heat_up.signedValue(),
        heat_down: heat_down.signedValue(),
        pick_init: pick_init.signedValue(),
        pick_up: pick_up.signedValue(),
        scan: scan.signedValue()
    }
}

export async function elevator_adj(type: number, offset: number) {
    return await conn.request(
        addr.MAIN,
        main.ELEVATOR_ADJ,
        new U8(type),
        new U16(offset)
    );
}

export async function cooking(
    col: number, 
    row: number, 
    option: number,
    tank: TankOption,
    clean: CleanOption,
    cooking_option: CookingOption) {
    
    const buf = make(
        new U8(col), new U8(row), new U8(option),
        new U8(tank.water), new U8(tank.steam),
        new U16(clean.c), new U16(clean.water), 
        new U16(clean.d), new U16(clean.steam),
        new U16(cooking_option.k), new U16(cooking_option.pre_water),
        new U16(cooking_option.f), new U16(cooking_option.g), new U16(cooking_option.defroze),
        new U16(cooking_option.e), new U16(cooking_option.pour_water),
        new U16(cooking_option.h), new U16(cooking_option.mix_soup),
        new U16(cooking_option.i), new U16(cooking_option.clogging) 
    );

    const msg = await conn.conn_request(
        addr.MAIN, 
        main.COOKING,
        buf
    );
    
    const src = new U8();
    const err = new U8();
    msg.parseResponse(src, err);

    if (err.value != 0) {
        throw err_msg_info(src.value, err.value);
    }
}

export async function set_idle_clean_arg(
    clean_interval: number,
    tank: TankOption,
    clean: CleanOption
) {
    return await conn.request(
        addr.MAIN, 
        main.IDLE_CLEAN_ARG,
        new U16(clean_interval),
        new U8(tank.water), new U8(tank.steam),
        new U16(clean.c), new U16(clean.water),
        new U16(clean.d), new U16(clean.steam)
    );
}

export interface IdleCleanInfo {
    clean_interval: number,
    tank: TankOption,
    clean: CleanOption,
}

export async function get_idle_clean_arg(): Promise<IdleCleanInfo> {
    const msg = await conn.conn_query(
        addr.MAIN, 
        main.IDLE_CLEAN_ARG
    );
    const clean_interval = new U16();
    const tank_water = new U16();
    const tank_steam = new U16();
    const c = new U16();
    const volume = new U16();
    const d = new U16();
    const steam = new U16();
    msg.parseResult(
        clean_interval, tank_water, tank_steam,
        c, volume, d, steam
    );

    return {
        clean_interval: clean_interval.value,
        tank: {
            water: tank_water.value,
            steam: tank_steam.value,
        },
        clean: {
            c: c.value,
            water: volume.value,
            d: d.value,
            steam: steam.value
        }
    }
}
